package com.example.springboot.config.anotation.autoware;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Objects;

/**
 * @Author:ChenZhangKun
 * @Date: 2023/6/18 15:30
 */
public class SuperAutowiredBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext=applicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(),field -> {
            injectBean(bean,field);
        });
        return bean;
    }

    private void injectBean(Object bean, Field field) {
        // 拿到注解
        SuperAutowired myAutowiredAnnotation  = field.getAnnotation(SuperAutowired.class);
        if (Objects.isNull(myAutowiredAnnotation)){
            return;
        }
        Object beanValue=null;
        // 设置可访问
        ReflectionUtils.makeAccessible(field);

        beanValue = proxyGetBean(field, myAutowiredAnnotation);

        // 配置属性注入
        if (beanValue == null){
            beanValue = propertiesGetBean(field, myAutowiredAnnotation);
        }

        // 类型注入
        if (beanValue == null){
            beanValue = autowireGetBean(field, myAutowiredAnnotation);
        }

        if (beanValue == null){
            throw new RuntimeException("无法找到对应的实现类");
        }

        ReflectionUtils.setField(field, bean,beanValue);

    }

    private Object proxyGetBean(Field field, SuperAutowired myAutowiredAnnotation) {
        if (myAutowiredAnnotation.proxy().length <= 0) {
            return null;
        }
        Class<? extends SuperAutowiredProxy> proxyClass = myAutowiredAnnotation.proxy()[0];
        SuperAutowiredProxy proxy = applicationContext.getBean(proxyClass);
        return proxy.getBean(myAutowiredAnnotation, applicationContext,field.getType());

    }
    private Object propertiesGetBean(Field field, SuperAutowired myAutowiredAnnotation) {
        if ("".equals(myAutowiredAnnotation.propValue())){
            return null;
        }

        Environment environment = applicationContext.getEnvironment();
        String className = environment.getProperty(myAutowiredAnnotation.propValue());
        if (className == null || Objects.equals(className, "")){
            throw new RuntimeException("该属性值" + myAutowiredAnnotation.propValue() + "为空无法找不到对应的实现类");
        }

        if (className.contains(".")){
            // 当类名处理
            Class<?> beanClass = loadClass(className);
            return getSameBean(beanClass);
        }else {
            // 当bean类处理
            return applicationContext.getBean(className);
        }
    }

    private Class<?> loadClass(String className) {
        try {
            Class<?> beanClass = ClassUtils.getDefaultClassLoader().loadClass(className);
            return beanClass;
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }


    private Object autowireGetBean(Field field, SuperAutowired myAutowiredAnnotation) {
        Object beanValue;
        Class<?> fieldType = field.getType();
        if (fieldType.isInterface() || Modifier.isAbstract(fieldType.getModifiers())){
            // 如果是接口按 传统autowired注入. 但是可能会有多个实现类. 一般只有一个实现类就不用指定 type了
            try {
                beanValue = applicationContext.getBean(fieldType);
            } catch (NoUniqueBeanDefinitionException e) {
                Class<?> type = myAutowiredAnnotation.type();
                if (type != Void.class){
                    beanValue = getSameBean(type);
                }else{
                    throw e;
                }
            }
        }else{
            // 如果是具体类则具体注入
            beanValue = getSameBean(fieldType);
        }
        return beanValue;
    }
    private Object getSameBean(Class<?> fieldType) {
        Map<String, ?> beansOfType = applicationContext.getBeansOfType(fieldType);
        if (beansOfType.size() <= 0){
            throw new RuntimeException("没有找到实现类: " + fieldType.getName());
        }

        for (Object value : beansOfType.values()) {
            if (isSameClass(value.getClass(), fieldType)) {
                return value;
            }
        }
        throw new RuntimeException("有多个实现类请指定: " + fieldType.getName());
    }

    public boolean isSameClass(Class<?> class1, Class<?> class2) {
        if (class1 == class2) {
            return true;
        }
        if (class2.isAssignableFrom(class1) || class1.isAssignableFrom(class2)) {
            return false;
        }
        return false;
    }


}
